package com.fangcang.b2b.wxpay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.fangcang.b2b.wxpay.domain.WxPayConfigDO;
import com.fangcang.b2b.wxpay.domain.WxPayNotifyDO;
import com.fangcang.b2b.wxpay.domain.WxPayRecordDO;
import com.fangcang.b2b.wxpay.dto.AccountPayResponseDTO;
import com.fangcang.b2b.wxpay.dto.OrderQueryResponseDTO;
import com.fangcang.b2b.wxpay.dto.WxH5PayResponseDTO;
import com.fangcang.b2b.wxpay.dto.WxOrderQueryRequestDTO;
import com.fangcang.b2b.wxpay.dto.WxOrderQueryResponseDTO;
import com.fangcang.b2b.wxpay.dto.WxPrepayRequestDTO;
import com.fangcang.b2b.wxpay.dto.WxPrepayResponseDTO;
import com.fangcang.b2b.wxpay.dto.WxWithdrawRequestDTO;
import com.fangcang.b2b.wxpay.dto.WxWithdrawResponseDTO;
import com.fangcang.b2b.wxpay.enums.WxPayTypeEnum;
import com.fangcang.b2b.wxpay.enums.WxURLConstant;
import com.fangcang.b2b.wxpay.mapper.WxPayConfigMapper;
import com.fangcang.b2b.wxpay.mapper.WxPayNotifyMapper;
import com.fangcang.b2b.wxpay.mapper.WxPayRecordMapper;
import com.fangcang.b2b.wxpay.service.WxPayService;
import com.fangcang.b2b.wxpay.util.CommonUtil;
import com.fangcang.b2b.wxpay.util.PayCommonUtil;
import com.fangcang.b2b.wxpay.util.WxPayUtil;
import com.fangcang.common.ResponseDTO;
import com.fangcang.common.enums.ResultCodeEnum;
import com.fangcang.common.exception.ServiceException;
import com.fangcang.message.remote.enums.WxAuth2ErrorEnum;
import com.fangcang.message.remote.WxAuth2Remote;
import com.fangcang.message.remote.response.weixin.WxConfigResponseDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

@Slf4j
@Service("wxPayService")
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private WxPayNotifyMapper wxPayNotifyMapper;
    @Autowired
    private WxPayRecordMapper wxPayRecordMapper;
    @Autowired
    private WxPayConfigMapper wxPayConfigMapper;
    @Autowired
    private WxAuth2Remote wxAuth2Remote;

    /**
     * 下微信预付单
     */
    public WxPrepayResponseDTO createWechatPrepayInfo(WxPrepayRequestDTO request) {
        ResponseDTO<List<WxConfigResponseDTO>> wxConfigResponse=wxAuth2Remote.queryWxConfig();
        if(wxConfigResponse.getResult()== ResultCodeEnum.FAILURE.code
                || wxConfigResponse.getModel()==null || wxConfigResponse.getModel().size()==0
                || StringUtils.isBlank(wxConfigResponse.getModel().get(0).getAppid())){
            throw new ServiceException(WxAuth2ErrorEnum.NO_BIND_WEIXIN_SERVICENO.getErrorMsg());
        }
        request.setAppId(wxConfigResponse.getModel().get(0).getAppid());

        List<WxPayConfigDO> payConfigDOList=wxPayConfigMapper.selectAll();
        if(payConfigDOList==null || payConfigDOList.size()==0){
            throw new ServiceException("微信支付信息未配置");
        }

        WxPrepayResponseDTO response = new WxPrepayResponseDTO();

        //获取微信下单参数
        SortedMap<Object, Object> sortMap = getWechatPrePayParams(request, payConfigDOList.get(0));
        response.setOrderCode(request.getOrderCode());
        response.setWeixinOutTradeNo(sortMap.get("out_trade_no").toString());

        //下微信预付单
        String requestXML = PayCommonUtil.getRequestXml(sortMap);
        String wxResult = CommonUtil.httpsRequest(WxURLConstant.UNIFIED_ORDER_URL, "POST", requestXML);

        //解析微信统一下单返回的信息
        Object wxResponseObj = CommonUtil.getClsFromWxResponse(wxResult, "AccountPayResponseDTO");
        AccountPayResponseDTO wxResponse = null;
        if(null != wxResponseObj) {
            wxResponse = (AccountPayResponseDTO) wxResponseObj;
            log.info("下微信预付单返回信息：" + wxResponse);
            if (wxResponse.getReturn_code().equalsIgnoreCase("SUCCESS")) {
                if(wxResponse.getResult_code().equalsIgnoreCase("SUCCESS")){
                    String prepayId = wxResponse.getPrepay_id();
                    if (StringUtils.isNotBlank(prepayId)) {
                        response.setPerpayId(prepayId);
                        response.setMwebUrl(wxResponse.getMweb_url());
                        //获取微信H5支付参数
                        SortedMap<Object, Object> h5PaySortMap = getWechatH5PayParams(request, payConfigDOList.get(0), prepayId);
                        WxH5PayResponseDTO weiXinH5PayVo = new WxH5PayResponseDTO();
                        weiXinH5PayVo.setAppId(h5PaySortMap.get("appId").toString());
                        weiXinH5PayVo.setNonceStr(h5PaySortMap.get("nonceStr").toString());
                        weiXinH5PayVo.setOrderPackage(h5PaySortMap.get("package").toString());
                        weiXinH5PayVo.setPaySign(h5PaySortMap.get("paySign").toString());
                        weiXinH5PayVo.setTimeStamp(h5PaySortMap.get("timeStamp").toString());
                        weiXinH5PayVo.setSignType(h5PaySortMap.get("signType").toString());
                        response.setWeiXinH5PayVo(weiXinH5PayVo);
                    } else {
                        throw new ServiceException("微信统一下单出现错误，请联系管理员！");
                    }
                } else {
                    throw new ServiceException(wxResponse.getErr_code_des());
                }
            } else {
                throw new ServiceException(wxResponse.getReturn_msg());
            }
        } else {
            throw new ServiceException("微信统一下单出现错误，请联系管理员！");
        }
        return response;
    }

    private SortedMap<Object, Object> getWechatPrePayParams(WxPrepayRequestDTO request, WxPayConfigDO wechatPayConfig) {
        //客户单号(格式：订单号+下划线+随机数)
        String weixinOutTradeNo=request.getOrderCode() + "_" + WxPayUtil.getOrderRandom();

        StringBuffer sb = new StringBuffer();
        sb.append(request.getOrderCode()).append(",").append(weixinOutTradeNo).append(",").append(request.getCreator());
        String attach = sb.toString();	//附加数据

        //订单金额（换算成分）
        int orderAmountFee = request.getOrderAmount().multiply(new BigDecimal("100")).intValue();

        //微信预付单请求参数，详情请参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
        SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>();
        sortedMap.put("appid", request.getAppId());	//公众账号ID
        sortedMap.put("attach", attach);		//附加数据
		/*限制商品描述38个字，超出截取*/
        String productName=request.getProductName();
        if(StringUtils.isNotBlank(productName) && productName.length()>38){
            productName=productName.substring(0, 38);
        }
        sortedMap.put("body", productName);	//商品描述
        sortedMap.put("mch_id", wechatPayConfig.getAccountNo());	//商户号
        sortedMap.put("nonce_str", PayCommonUtil.CreateNoncestr());	//随机字符串
        sortedMap.put("notify_url", request.getNotifyUrl());	//异步通知回调地址
        sortedMap.put("out_trade_no", weixinOutTradeNo);	//商户订单号
        sortedMap.put("spbill_create_ip", request.getClientIp());	//终端IP
        sortedMap.put("total_fee", String.valueOf(orderAmountFee));	//总金额
        if(WxPayTypeEnum.MWEB.equals(request.getWechatPayType())){//交易类型
            sortedMap.put("trade_type", "MWEB");	//交易类型
            JSONObject h5Info = new JSONObject();
            h5Info.put("type", "Wap");
            h5Info.put("wap_url", request.getWapUrl());
            h5Info.put("wap_name", request.getWapName());

            JSONObject sceneInfo = new JSONObject();
            sceneInfo.put("h5_info", h5Info);

            sortedMap.put("scene_info", sceneInfo.toJSONString());
        }else{
            sortedMap.put("trade_type", "JSAPI");
            sortedMap.put("openid", request.getOpenId());		//微信用户标识
        }

        String apiKey = wechatPayConfig.getApiKey();	//API密钥
        String sign = PayCommonUtil.createSign("UTF-8", sortedMap, apiKey);	//签名
        sortedMap.put("sign", sign);

        return sortedMap;
    }

    private SortedMap<Object, Object> getWechatH5PayParams(WxPrepayRequestDTO request, WxPayConfigDO wechatPayConfig, String prepayId) {
        //微信网页端调起支付API，详情请参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
        SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>();
        sortedMap.put("appId", request.getAppId());	//公众账号ID
//        sortedMap.put("timeStamp", Long.toString(new Date().getTime()));	//时间戳
        sortedMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
        sortedMap.put("nonceStr", PayCommonUtil.CreateNoncestr());	//随机字符串
        sortedMap.put("package", "prepay_id=" + prepayId);	//订单详情扩展字符串
        sortedMap.put("signType", "MD5");	//签名方式

        String apiKey = wechatPayConfig.getApiKey();	//API密钥
        String paySign = PayCommonUtil.createSign("UTF-8", sortedMap, apiKey);
        sortedMap.put("paySign", paySign);	//签名

        return sortedMap;
    }

    /**
     * 查询微信订单接口，主动查询订单状态
     * @param request
     * @return
     */
    public WxOrderQueryResponseDTO queryOrderInfo(WxOrderQueryRequestDTO request){
        ResponseDTO<List<WxConfigResponseDTO>> wxConfigResponse=wxAuth2Remote.queryWxConfig();
        if(wxConfigResponse.getResult()== ResultCodeEnum.FAILURE.code
                || wxConfigResponse.getModel()==null || wxConfigResponse.getModel().size()==0
                || StringUtils.isBlank(wxConfigResponse.getModel().get(0).getAppid())){
            throw new ServiceException(WxAuth2ErrorEnum.NO_BIND_WEIXIN_SERVICENO.getErrorMsg());
        }
        request.setAppId(wxConfigResponse.getModel().get(0).getAppid());

        List<WxPayConfigDO> payConfigDOList=wxPayConfigMapper.selectAll();
        if(payConfigDOList==null || payConfigDOList.size()==0){
            throw new ServiceException("微信支付信息未配置");
        }

        WxOrderQueryResponseDTO response = new WxOrderQueryResponseDTO();

        //获取微信下单参数
        SortedMap<Object, Object> sortMap = getWeiXinOrderQueryParams(request, payConfigDOList.get(0));

        //查询微信订单接口
        String requestXML = PayCommonUtil.getRequestXml(sortMap);
        String wxResult = CommonUtil.httpsRequest(WxURLConstant.CHECK_ORDER_URL, "POST", requestXML);

        //解析查询微信订单返回的信息
        Object wxResponseObj = CommonUtil.getClsFromWxResponse(wxResult, "OrderQueryResponseDTO");
        OrderQueryResponseDTO wxResponse = null;
        if(null != wxResponseObj) {
            wxResponse = (OrderQueryResponseDTO) wxResponseObj;
            log.info("查询微信订单接口返回信息：" + wxResponse);
            if (wxResponse.getReturn_code().equalsIgnoreCase("SUCCESS")) {
                if(wxResponse.getResult_code().equalsIgnoreCase("SUCCESS")){
                    response.setTrade_state(wxResponse.getTrade_state());
                } else {
                    throw new ServiceException(wxResponse.getErr_code_des());
                }
            } else {
                throw new ServiceException(wxResponse.getReturn_msg());
            }
        } else {
            throw new ServiceException("查询微信订单接口出现错误，请联系管理员！");
        }
        return response;
    }

    /**
     * 获取查询微信订单API参数
     * @param request
     * @param wechatPayConfig
     * @return
     */
    private SortedMap<Object, Object> getWeiXinOrderQueryParams(WxOrderQueryRequestDTO request, WxPayConfigDO wechatPayConfig) {
        SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>();
        sortedMap.put("appid", request.getAppId());	//公众账号ID
        sortedMap.put("mch_id", wechatPayConfig.getAccountNo());	//商户号	-wechatPayConfig
        sortedMap.put("nonce_str", PayCommonUtil.CreateNoncestr());	//随机字符串
        sortedMap.put("out_trade_no", request.getWeixinOutTradeNo());	//客户单号-request
        String apiKey = wechatPayConfig.getApiKey();	//API密钥=商户key

        String sign = PayCommonUtil.createSign("UTF-8", sortedMap, apiKey);	//签名
        sortedMap.put("sign", sign);

        log.info("------>获取查询微信订单API参数-apiKey:"+apiKey+"\n sortedMap:"+sortedMap+"\n sign:"+sign);
        return sortedMap;
    }

    /**
     * 保存微信支付记录
     * @param wxPayRecordDO
     */
    public void saveWxPayRecord(WxPayRecordDO wxPayRecordDO){
        wxPayRecordMapper.insert(wxPayRecordDO);
    }

    public void updateWxPayRecordStatus(String weixinOutTradeNo,Integer status,String desc){
        WxPayRecordDO param=new WxPayRecordDO();
        param.setStatus(status);
        param.setBatchStatus(1);   //更新为已处理
        param.setBatchDesc(desc);
        Example example=new Example(WxPayRecordDO.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("weixinOutTradeNo",weixinOutTradeNo);
        wxPayRecordMapper.updateByExampleSelective(param,example);
    }

    public List<WxPayRecordDO> queryWxPayRecord(String orderCode,Integer status){
        WxPayRecordDO param=new WxPayRecordDO();
        param.setOrderCode(orderCode);
        param.setStatus(status);
        return wxPayRecordMapper.select(param);
    }

    /**
     * 保存微信支付回调记录
     * @param wxPayNotifyDO
     */
    public void saveWxPayNotify(WxPayNotifyDO wxPayNotifyDO){
        wxPayNotifyMapper.insert(wxPayNotifyDO);
    }

    public void updateWxPayNotifyStatus(String weixinOutTradeNo,Integer status,String resultDesc){
        WxPayNotifyDO param=new WxPayNotifyDO();
        param.setStatus(status);
        param.setResultDesc(resultDesc);   //更新为已处理
        Example example=new Example(WxPayNotifyDO.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("weixinOutTradeNo",weixinOutTradeNo);
        wxPayNotifyMapper.updateByExampleSelective(param,example);
    }

    /**
     * 查询微信支付配置
     * @return
     */
    public WxPayConfigDO queryWxPayConfig(){
        List<WxPayConfigDO> payConfigDOList=wxPayConfigMapper.selectAll();
        if(payConfigDOList==null || payConfigDOList.size()==0){
            throw new ServiceException("微信支付信息未配置");
        }
        return payConfigDOList.get(0);
    }

    /**
     * 微信提现
     * @return
     */
    public WxWithdrawResponseDTO wxWithdraw(WxWithdrawRequestDTO requestDTO){
        WxWithdrawResponseDTO responseDTO=new WxWithdrawResponseDTO();
        StringEntity entity;
        FileInputStream instream=null;
        CloseableHttpClient httpclient=null;
        try {
            //读取本机存放的PKCS12证书文件
            instream = new FileInputStream(new java.io.File("/data/cert/cert"+requestDTO.getMchid()+"/apiclient_cert.p12"));
            //指定读取证书格式为PKCS12
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //指定PKCS12的密码(商户ID)
            keyStore.load(instream, requestDTO.getMchid().toCharArray());

            @SuppressWarnings("deprecation")
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, requestDTO.getMchid().toCharArray()).build();
            //指定TLS版本
            @SuppressWarnings("deprecation")
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

            String check_name="NO_CHECK";
            String desc="佣金提现";
            String nonce_str=WxPayUtil.randomString(8);
            String partner_trade_no=WxPayUtil.createPartnerTradeNo();
            String spbill_create_ip=WxPayUtil.getIpAddress();

            SortedMap<Object,Object> parametersWxPay=new TreeMap<Object,Object>();
            parametersWxPay.put("amount", requestDTO.getAmount().multiply(new BigDecimal(100)));//支付金额
            parametersWxPay.put("check_name",check_name );//校验用户姓名
            parametersWxPay.put("desc",desc );//企业付款描述信息
            parametersWxPay.put("mch_appid", requestDTO.getMchAppid());//公众号id
            parametersWxPay.put("mchid", requestDTO.getMchid());//商户号
            parametersWxPay.put("nonce_str",nonce_str );//随机字符串
            parametersWxPay.put("openid",requestDTO.getOpenid());//用户openid
            parametersWxPay.put("partner_trade_no",partner_trade_no );//生成商户订单号唯一
            parametersWxPay.put("spbill_create_ip",spbill_create_ip );//获取当前用户ip地址
            String sign = WxPayUtil.createSign("utf-8", parametersWxPay, requestDTO.getApiKey());//生成签名
            //拼接xml格式数据
            StringBuilder xml = new StringBuilder();
            xml.append("<xml>\n");
            xml.append("<mch_appid>"+requestDTO.getMchAppid()+"</mch_appid>\n");
            xml.append("<mchid>"+requestDTO.getMchid()+"</mchid>\n");
            xml.append("<nonce_str>"+nonce_str+"</nonce_str>\n");
            xml.append("<partner_trade_no>"+partner_trade_no+"</partner_trade_no>\n");
            xml.append("<openid>"+requestDTO.getOpenid()+"</openid>\n");
            xml.append("<check_name>"+check_name+"</check_name>\n");
            // xml.append("<re_user_name>"+wxPayBean.getReUserName()+"</re_user_name>\n");
            xml.append("<amount>"+requestDTO.getAmount().multiply(new BigDecimal(100))+"</amount>\n");
            xml.append("<desc>"+desc+"</desc>\n");
            xml.append("<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>\n");
            xml.append("<sign>"+sign+"</sign>\n");
            xml.append("</xml>");
            entity = new StringEntity(xml.toString(), "utf-8");

            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers");
            httpPost.setEntity(entity);//设置发送数据
            HttpResponse response = httpclient.execute(httpPost);//发送请求
            HttpEntity httpEntity = response.getEntity();
            // 打印响应内容
            String result = EntityUtils.toString(httpEntity, "UTF-8");

            //result = result.replaceAll("<![CDATA[|]]>", "");
            //String resultCode = Jsoup.parse(result).select("result_code").html();

            if(StringUtils.isNotBlank(result)) {
                Map<String, String> noticeDatas = WxPayUtil.xmlToMap(result);
                String resultCode=noticeDatas.get("result_code");
                log.info("resultCode := " + resultCode);
                if ("SUCCESS".equals(resultCode)) {//支付成功

                }else{

                }
            }
        } catch (FileNotFoundException |KeyStoreException |NoSuchAlgorithmException
                |CertificateException |UnrecoverableKeyException |KeyManagementException e) {
            log.error("读取证书文件异常",e);
            throw new ServiceException("读取证书文件异常",e);
        } catch (IOException e) {
            log.error("调用微信接口异常",e);
            throw new ServiceException("调用微信接口异常",e);
        } catch (Exception e) {
            throw new ServiceException(e);
        } finally {
            try {
                // 关闭文件,释放资源
                instream.close();
            } catch (IOException e) {
                throw new ServiceException(e);
            }
            try {
                // 关闭连接,释放资源
                httpclient.close();
            } catch (IOException e) {
                throw new ServiceException(e);
            }
        }
        return responseDTO;
    }
}
